home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Html / ^stIHEditor / setup.exe / {app} / tinyweb / SRC.ZIP / SRVMAIN.PAS next >
Encoding:
Pascal/Delphi Source File  |  2000-01-14  |  57.3 KB  |  1,966 lines

  1. //////////////////////////////////////////////////////////////////////////
  2. //
  3. //  TinyWeb Copyright (C) 1997-2000 RIT Research Labs
  4. //
  5. //  This programs is free for commercial and non-commercial use as long as
  6. //  the following conditions are aheared to.
  7. //
  8. //  Copyright remains RIT Research Labs, and as such any Copyright notices
  9. //  in the code are not to be removed. If this package is used in a
  10. //  product, RIT Research Labs should be given attribution as the RIT Research
  11. //  Labs of the parts of the library used. This can be in the form of a textual
  12. //  message at program startup or in documentation (online or textual)
  13. //  provided with the package.
  14. //
  15. //  Redistribution and use in source and binary forms, with or without
  16. //  modification, are permitted provided that the following conditions are
  17. //  met:
  18. //
  19. //  1. Redistributions of source code must retain the copyright
  20. //     notice, this list of conditions and the following disclaimer.
  21. //  2. Redistributions in binary form must reproduce the above copyright
  22. //     notice, this list of conditions and the following disclaimer in the
  23. //     documentation and/or other materials provided with the distribution.
  24. //  3. All advertising materials mentioning features or use of this software
  25. //     must display the following acknowledgement:
  26. //     "Based on TinyWeb Server by RIT Research Labs."
  27. //
  28. //  THIS SOFTWARE IS PROVIDED BY RIT RESEARCH LABS "AS IS" AND ANY EXPRESS
  29. //  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  30. //  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. //  DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
  32. //  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  33. //  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  34. //  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  35. //  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  36. //  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  37. //  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  38. //  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  39. //
  40. //  The licence and distribution terms for any publically available
  41. //  version or derivative of this code cannot be changed. i.e. this code
  42. //  cannot simply be copied and put under another distribution licence
  43. //  (including the GNU Public Licence).
  44. //
  45. //////////////////////////////////////////////////////////////////////////
  46.  
  47.  
  48. unit SrvMain;
  49.  
  50. interface
  51.  
  52. procedure ComeOn;
  53.  
  54. implementation
  55.  
  56. uses
  57.   WinSock,
  58.   Windows,
  59.   xBase;
  60.  
  61. const
  62.  
  63.   ScriptsPath = 'cgi-bin';
  64.  
  65.   CHTTPServerThreadBufSize = $2000;
  66.   MaxStatusCodeIdx = 36;
  67.   StatusCodes : array[0..MaxStatusCodeIdx] of record Code: Integer; Msg: string end =
  68.   ((Code:100; Msg:'Continue'),
  69.    (Code:101; Msg:'Switching Protocols'),
  70.    (Code:200; Msg:'OK'),
  71.    (Code:201; Msg:'Created'),
  72.    (Code:202; Msg:'Accepted'),
  73.    (Code:203; Msg:'Non-Authoritative Information'),
  74.    (Code:204; Msg:'No Content'),
  75.    (Code:205; Msg:'Reset Content'),
  76.    (Code:206; Msg:'Partial Content'),
  77.    (Code:300; Msg:'Multiple Choices'),
  78.    (Code:301; Msg:'Moved Permanently'),
  79.    (Code:302; Msg:'Moved Temporarily'),
  80.    (Code:303; Msg:'See Other'),
  81.    (Code:304; Msg:'Not Modified'),
  82.    (Code:305; Msg:'Use Proxy'),
  83.    (Code:400; Msg:'Bad Request'),
  84.    (Code:401; Msg:'Unauthorized'),
  85.    (Code:402; Msg:'Payment Required'),
  86.    (Code:403; Msg:'Forbidden'),
  87.    (Code:404; Msg:'Not Found'),
  88.    (Code:405; Msg:'Method Not Allowed'),
  89.    (Code:406; Msg:'Not Acceptable'),
  90.    (Code:407; Msg:'Proxy Authentication Required'),
  91.    (Code:408; Msg:'Request Time-out'),
  92.    (Code:409; Msg:'Conflict'),
  93.    (Code:410; Msg:'Gone'),
  94.    (Code:411; Msg:'Length Required'),
  95.    (Code:412; Msg:'Precondition Failed'),
  96.    (Code:413; Msg:'Request Entity Too Large'),
  97.    (Code:414; Msg:'Request-URI Too Large'),
  98.    (Code:415; Msg:'Unsupported Media Type'),
  99.    (Code:500; Msg:'Internal Server Error'),
  100.    (Code:501; Msg:'Not Implemented'),
  101.    (Code:502; Msg:'Bad Gateway'),
  102.    (Code:503; Msg:'Service Unavailable'),
  103.    (Code:504; Msg:'Gateway Time-out'),
  104.    (Code:505; Msg:'HTTP Version not supported'));
  105.  
  106. type
  107.   TEntityHeader = class;
  108.   TCollector = class;
  109.  
  110.   TAbstractHttpResponseData = class
  111.   end;
  112.  
  113.   THttpResponseDataFileHandle = class(TAbstractHttpResponseData)
  114.     FHandle: THandle;
  115.     constructor Create(AHandle: DWORD);
  116.   end;
  117.  
  118.   THttpResponseDataEntity = class(TAbstractHttpResponseData)
  119.     FEntityHeader : TEntityHeader;
  120.     constructor Create(AEntityHeader : TEntityHeader);
  121.   end;
  122.  
  123.   THttpResponseErrorCode = class(TAbstractHttpResponseData)
  124.     FErrorCode: Integer;
  125.     constructor Create(AErrorCode: Integer);
  126.   end;
  127.  
  128.   PHTTPServerThreadBufer = ^THTTPServerThreadBufer;
  129.   THTTPServerThreadBufer = array[0..CHTTPServerThreadBufSize-1] of Char;
  130.  
  131.   TPipeReadStdThread = class(TThread)
  132.     Error: Boolean;
  133.     HPipe: DWORD;
  134.     Buffer: PHTTPServerThreadBufer;
  135.     EntityHeader: TEntityHeader;
  136.     Collector: TCollector;
  137.     procedure Execute; override;
  138.   end;
  139.  
  140.   TPipeWriteStdThread = class(TThread)
  141.     HPipe: DWORD;
  142.     s: string;
  143.     procedure Execute; override;
  144.   end;
  145.  
  146.   TPipeReadErrThread = class(TThread)
  147.     HPipe: DWORD;
  148.     s: string;
  149.     procedure Execute; override;
  150.   end;
  151.  
  152.   TContentType = class
  153.     ContentType,
  154.     Extension: string;
  155.   end;
  156.  
  157.   TContentTypeColl = class(TSortedColl)
  158.     function Compare(Key1, Key2: Pointer): Integer; override;
  159.     function KeyOf(Item: Pointer): Pointer; override;
  160.   end;
  161.  
  162.   THTTPData = class;
  163.  
  164.   THTTPServerThread = class(TThread)
  165.     RemoteHost,
  166.     RemoteAddr: string;
  167.     Buffer: THTTPServerThreadBufer;
  168.     Socket: TSocket;
  169.     constructor Create;
  170.     procedure PrepareResponse(d: THTTPData);
  171.     procedure Execute; override;
  172.     destructor Destroy; override;
  173.   end;
  174.  
  175.   TGeneralHeader = class
  176.     CacheControl,            // Section 14.9
  177.     Connection,              // Section 14.10
  178.     Date,                    // Section 14.19
  179.     Pragma,                  // Section 14.32
  180.     TransferEncoding,        // Section 14.40
  181.     Upgrade,                 // Section 14.41
  182.     Via : string;            // Section 14.44
  183.     function Filter(const z, s: string): Boolean;
  184.     function OutString: string;
  185.   end;
  186.  
  187.  
  188.   TResponseHeader = class
  189.     Age,                    // Section 14.6
  190.     Location,               // Section 14.30
  191.     ProxyAuthenticate,      // Section 14.33
  192.     Public_,                // Section 14.35
  193.     RetryAfter,             // Section 14.38
  194.     Server,                 // Section 14.39
  195.     Vary,                   // Section 14.43
  196.     Warning,                // Section 14.45
  197.     WWWAuthenticate         // Section 14.46
  198.       : string;
  199.     function OutString: string;
  200.   end;
  201.  
  202.   TRequestHeader = class
  203.     Accept,                  // Section 14.1
  204.     AcceptCharset,           // Section 14.2
  205.     AcceptEncoding,          // Section 14.3
  206.     AcceptLanguage,          // Section 14.4
  207.     Authorization,           // Section 14.8
  208.     From,                    // Section 14.22
  209.     Host,                    // Section 14.23
  210.     IfModifiedSince,         // Section 14.24
  211.     IfMatch,                 // Section 14.25
  212.     IfNoneMatch,             // Section 14.26
  213.     IfRange,                 // Section 14.27
  214.     IfUnmodifiedSince,       // Section 14.28
  215.     MaxForwards,             // Section 14.31
  216.     ProxyAuthorization,      // Section 14.34
  217.     Range,                   // Section 14.36
  218.     Referer,                 // Section 14.37
  219.     UserAgent,               // Section 14.42
  220.     Cookie: string;          // rfc-2109
  221.     function Filter(const z, s: string): Boolean;
  222.   end;
  223.  
  224.   TCollector = class
  225.   private
  226.     Parsed: Boolean;
  227.     Lines: TStringColl;
  228.     CollectStr: string;
  229.     CollectLen: Integer;
  230.     ContentLength: Integer;
  231.   public
  232.     EntityBody: string;
  233.     GotEntityBody,
  234.     CollectEntityBody: Boolean;
  235.     function Collect(var Buf: THTTPServerThreadBufer; j: Integer): Boolean;
  236.     constructor Create;
  237.     destructor Destroy; override;
  238.     function GetNextLine: string;
  239.     function LineAvail: Boolean;
  240.     procedure SetContentLength(i: Integer);
  241.   end;
  242.  
  243.  
  244.   TEntityHeader = class
  245.     Allow,                   // Section 14.7
  246.     ContentBase,             // Section 14.11
  247.     ContentEncoding,         // Section 14.12
  248.     ContentLanguage,         // Section 14.13
  249.     ContentLength,           // Section 14.14
  250.     ContentLocation,         // Section 14.15
  251.     ContentMD5,              // Section 14.16
  252.     ContentRange,            // Section 14.17
  253.     ContentType,             // Section 14.18
  254.     ETag,                    // Section 14.20
  255.     Expires,                 // Section 14.21
  256.     LastModified,            // Section 14.29
  257.     EntityBody: string;
  258.     EntityLength: Integer;
  259.     SetCookie,
  260.     CGIStatus,
  261.     CGILocation: string;
  262.     function Filter(const z, s: string): Boolean;
  263.     procedure CopyEntityBody(Collector: TCollector);
  264.     function OutString: string;
  265.   end;
  266.  
  267.   THTTPData = class
  268.     RequestCollector: TCollector;
  269.     FileNfo: TFileINfo;
  270.  
  271.     FHandle: THandle;
  272.     StatusCode,
  273.     HTTPVersionHi,
  274.     HTTPVersionLo: Integer;
  275.  
  276.     TransferFile,
  277.     ReportError,
  278.     KeepAlive: Boolean;
  279.  
  280.     ErrorMsg,
  281.     Method, RequestURI, HTTPVersion, AuthUser, AuthPassword, AuthType,
  282.     URIPath, URIParams, URIQuery, URIQueryParam : string;
  283.  
  284.     ResponceObjective: TAbstractHttpResponseData;
  285.  
  286.     RequestGeneralHeader: TGeneralHeader;
  287.     RequestRequestHeader: TRequestHeader;
  288.     RequestEntityHeader: TEntityHeader;
  289.  
  290.     ResponseGeneralHeader: TGeneralHeader;
  291.     ResponseResponseHeader: TResponseHeader;
  292.     ResponseEntityHeader: TEntityHeader;
  293.  
  294.     constructor Create;
  295.     destructor Destroy; override;
  296.  
  297.   end;
  298.  
  299. var
  300.   ContentTypes: TContentTypeColl;
  301.   ParamStr1,
  302.   FAccessLog,
  303.   FAgentLog,
  304.   FErrorLog,
  305.   FRefererLog: string;
  306.   CSAccessLog,
  307.   CSAgentLog,
  308.   CSErrorLog,
  309.   CSRefererLog: TRTLCriticalSection;
  310.   HAccessLog,
  311.   HAgentLog,
  312.   HErrorLog,
  313.   HRefererLog: DWORD;
  314.  
  315.  
  316. function FileTimeToStr(AT: DWORD): string;
  317. const
  318.   wkday: array[0..6] of string = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
  319. var
  320.   D: TSystemTime;
  321.   T: TFileTime;
  322. begin
  323.   uCvtSetFileTime(AT, T.dwLowDateTime, T.dwHighDateTime);
  324.   if FileTimeToSystemTime(T, D) then
  325.   Result :=
  326.   wkday[D.wDayOfWeek] + ', ' +
  327.   ItoSz(D.wDay, 2) + ' ' +
  328.   MonthE(D.wMonth) + ' ' +
  329.   ItoS(D.wYear) + ' ' +
  330.   ItoSz(D.wHour, 2) + ':' +
  331.   ItoSz(D.wMinute, 2) + ':' +
  332.   ItoSz(D.wSecond, 2) + ' GMT';
  333. end;
  334.  
  335. function StrToFileTime(AStr: string): DWORD;
  336. var
  337.   D: TSystemTime;
  338.   T: TFileTime;
  339.   s, z: string;
  340.   e: Integer;
  341. begin
  342.   Result := INVALID_FILE_TIME;
  343.   Clear(D, SizeOf(D));
  344.   s := AStr;
  345.   GetWrd(s, z, ' ');
  346.   GetWrdD(s, z); Val(z, D.wDay, e); if e > 0 then Exit;
  347.   GetWrdA(s, z); D.wMonth := Pos(#1+UpperCase(z)+#1, #1'JAN'#1'FEB'#1'MAR'#1'APR'#1'MAY'#1'JUN'#1'JUL'#1'AUG'#1'SEP'#1'OCT'#1'NOV'#1'DEC'#1);
  348.   if D.wMonth = 0 then Exit;
  349.   D.wMonth := (D.wMonth+3) div 4;
  350.   GetWrdD(s, z); Val(z, D.wYear, e); if e > 0 then Exit;
  351.   if D.wYear < 200 then
  352.   begin
  353.     if D.wYear < 50 then Inc(D.wYear, 2000) else Inc(D.wYear, 1900);
  354.   end;
  355.   GetWrdD(s, z); Val(z, D.wHour, e); if e > 0 then Exit;
  356.   GetWrdD(s, z); Val(z, D.wMinute, e); if e > 0 then Exit;
  357.   GetWrdD(s, z); Val(z, D.wSecond, e); if e > 0 then Exit;
  358.   if not SystemTimeToFileTime(D, T) then Exit;
  359.   Result := uCvtGetFileTime(T.dwLowDateTime, T.dwHighDateTime);
  360. end;
  361.  
  362. // 'Sunday, 17-May-98 18:44:23 GMT; length=4956'
  363.  
  364. constructor THTTPServerThread.Create;
  365. begin
  366.   inherited Create(True);
  367. end;
  368.  
  369. destructor THTTPServerThread.Destroy;
  370. begin
  371.   FreeObject(Socket);
  372.   inherited Destroy;
  373. end;
  374.  
  375. function TGeneralHeader.Filter(const z, s: string): Boolean;
  376. begin
  377.   Result := True;
  378.   if z = 'CACHE-CONTROL'       then CacheControl       := s else // Section 14.9
  379.   if z = 'CONNECTION'          then Connection         := s else // Section 14.10
  380.   if z = 'DATE'                then Date               := s else // Section 14.19
  381.   if z = 'PRAGMA'              then Pragma             := s else // Section 14.32
  382.   if z = 'TRANSFER-ENCODING'   then TransferEncoding   := s else // Section 14.40
  383.   if z = 'UPGRADE'             then Upgrade            := s else // Section 14.41
  384.   if z = 'VIA'                 then Via                := s else // Section 14.44
  385.     Result := False;
  386. end;
  387.  
  388. function TRequestHeader.Filter(const z, s: string): Boolean;
  389. begin
  390.   Result := True;
  391.   if z = 'ACCEPT'              then Accept             := s else // Section 14.1
  392.   if z = 'ACCEPT-CHARSET'      then AcceptCharset      := s else // Section 14.2
  393.   if z = 'ACCEPT-ENCODING'     then AcceptEncoding     := s else // Section 14.3
  394.   if z = 'ACCEPT-LANGUAGE'     then AcceptLanguage     := s else // Section 14.4
  395.   if z = 'AUTHORIZATION'       then Authorization      := s else // Section 14.8
  396.   if z = 'FROM'                then From               := s else // Section 14.22
  397.   if z = 'HOST'                then Host               := s else // Section 14.23
  398.   if z = 'IF-MODIFIED-SINCE'   then IfModifiedSince    := s else // Section 14.24
  399.   if z = 'IF-MATCH'            then IfMatch            := s else // Section 14.25
  400.   if z = 'IF-NONE-MATCH'       then IfNoneMatch        := s else // Section 14.26
  401.   if z = 'IF-RANGE'            then IfRange            := s else // Section 14.27
  402.   if z = 'IF-UNMODIFIED-SINCE' then IfUnmodifiedSince  := s else // Section 14.28
  403.   if z = 'MAX-FORWARDS'        then MaxForwards        := s else // Section 14.31
  404.   if z = 'PROXY-AUTHORIZATION' then ProxyAuthorization := s else // Section 14.34
  405.   if z = 'RANGE'               then Range              := s else // Section 14.36
  406.   if z = 'REFERER'             then Referer            := s else // Section 14.37
  407.   if z = 'USER-AGENT'          then UserAgent          := s else // Section 14.42
  408.   if z = 'COOKIE'              then Cookie             := s else
  409.     Result := False
  410. end;
  411.  
  412. procedure Add(var s, z: string; const a: string);
  413. begin
  414.   if z <> '' then s := s + a + ': '+z+#13#10;
  415. end;
  416.  
  417. function TResponseHeader.OutString: string;
  418. var
  419.   s: string;
  420. begin
  421.   s := '';
  422.   Add(s, Age,               'Age');                // Section 14.6
  423.   Add(s, Location,          'Location');           // Section 14.30
  424.   Add(s, ProxyAuthenticate, 'Proxy-Authenticate'); // Section 14.33
  425.   Add(s, Public_,           'Public');             // Section 14.35
  426.   Add(s, RetryAfter,        'Retry-After');        // Section 14.38
  427.   Add(s, Server,            'Server');             // Section 14.39
  428.   Add(s, Vary,              'Vary');               // Section 14.43
  429.   Add(s, Warning,           'Warning');            // Section 14.45
  430.   Add(s, WWWAuthenticate,   'WWW-Authenticate');   // Section 14.46
  431.   Result := s;
  432. end;
  433.  
  434. function TEntityHeader.OutString: string;
  435. var
  436.   s: string;
  437. begin
  438.   s := '';
  439.   Add(s, Allow,           'Allow');             // Section 14.7
  440.   Add(s, ContentBase,     'Content-Base');      // Section 14.11
  441.   Add(s, ContentEncoding, 'Content-Encoding');  // Section 14.12
  442.   Add(s, ContentLanguage, 'Content-Language');  // Section 14.13
  443.   Add(s, ContentLength,   'Content-Length');    // Section 14.14
  444.   Add(s, ContentLocation, 'Content-Location');  // Section 14.15
  445.   Add(s, ContentMD5,      'Content-MD5');       // Section 14.16
  446.   Add(s, ContentRange,    'Content-Range');     // Section 14.17
  447.   Add(s, ContentType,     'Content-Type');      // Section 14.18
  448.   Add(s, ETag,            'ETag');              // Section 14.20
  449.   Add(s, Expires,         'Expires');           // Section 14.21
  450.   Add(s, LastModified,    'Last-Modified');     // Section 14.29
  451.   Add(s, SetCookie,       'Set-Cookie');
  452.   Result := s;
  453. end;
  454.  
  455. function TGeneralHeader.OutString: string;
  456. var
  457.   s: string;
  458. begin
  459.   s := '';
  460.   Add(s, CacheControl,     'Cache-Control');     // Section 14.9
  461.   Add(s, Connection,       'Connection');        // Section 14.10
  462.   Add(s, Date,             'Date');              // Section 14.19
  463.   Add(s, Pragma,           'Pragma');            // Section 14.32
  464.   Add(s, TransferEncoding, 'Transfer-Encoding'); // Section 14.40
  465.   Add(s, Upgrade,          'Upgrade');           // Section 14.41
  466.   Add(s, Via,              'Via');               // Section 14.44
  467.   Result := s;
  468. end;
  469.  
  470. procedure TEntityHeader.CopyEntityBody(Collector: TCollector);
  471. begin
  472.   EntityLength := Collector.ContentLength;
  473.   ContentLength := ItoS(Collector.ContentLength);
  474.   EntityBody := Copy(Collector.EntityBody, 1, EntityLength);
  475. end;
  476.  
  477. function TEntityHeader.Filter(const z, s: string): Boolean;
  478. begin
  479.   Result := True;
  480.   if z = 'ALLOW'            then Allow           := s else // 14.7
  481.   if z = 'CONTENT-BASE'     then ContentBase     := s else // 14.11
  482.   if z = 'CONTENT-ENCODING' then ContentEncoding := s else // 14.12
  483.   if z = 'CONTENT-LANGUAGE' then ContentLanguage := s else // 14.13
  484.   if z = 'CONTENT-LENGTH'   then ContentLength   := s else // 14.14
  485.   if z = 'CONTENT-LOCATION' then ContentLocation := s else // 14.15
  486.   if z = 'CONTENT-MD5'      then ContentMD5      := s else // 14.16
  487.   if z = 'CONTENT-RANGE'    then ContentRange    := s else // 14.17
  488.   if z = 'CONTENT-TYPE'     then ContentType     := s else // 14.18
  489.   if z = 'ETAG'             then ETag            := s else // 14.20
  490.   if z = 'EXPIRES'          then Expires         := s else // 14.21
  491.   if z = 'LAST-MODIFIED'    then LastModified    := s else // 14.29
  492.   if z = 'STATUS'           then
  493.   CGIStatus       := s
  494.   else
  495.   if z = 'LOCATION'         then CGILocation     := s else
  496.   if z = 'SET-COOKIE'       then SetCookie       := s else
  497.     Result := False;
  498. end;
  499.  
  500. constructor THTTPData.Create;
  501. begin
  502.   inherited Create;
  503.   RequestCollector := TCollector.Create;
  504.   RequestGeneralHeader := TGeneralHeader.Create;
  505.   RequestRequestHeader := TRequestHeader.Create;
  506.   RequestEntityHeader := TEntityHeader.Create;
  507. end;
  508.  
  509. destructor THTTPData.Destroy;
  510. begin
  511.   FreeObject(RequestCollector);
  512.   FreeObject(RequestGeneralHeader);
  513.   FreeObject(RequestRequestHeader);
  514.   FreeObject(RequestEntityHeader);
  515.   FreeObject(ResponseGeneralHeader);
  516.   FreeObject(ResponseResponseHeader);
  517.   FreeObject(ResponseEntityHeader);
  518.   ZeroHandle(FHandle);
  519.   inherited Destroy;
  520. end;
  521.  
  522. procedure TCollector.SetContentLength(i: Integer);
  523. begin
  524.   ContentLength := i;
  525.   GotEntityBody := ContentLength <= Length(EntityBody);
  526. end;
  527.  
  528. function TCollector.LineAvail: Boolean;
  529. begin
  530.   Result := Lines.Count > 0;
  531. end;
  532.  
  533. function TCollector.GetNextLine: string;
  534. begin
  535.   Result := Lines[0]; Lines.AtFree(0);
  536. end;
  537.  
  538. function TCollector.Collect(var Buf: THTTPServerThreadBufer; j: Integer): Boolean;
  539. var
  540.   i,l: Integer;
  541. begin
  542.   if not CollectEntityBody then
  543.   begin
  544.     l := Length(CollectStr);
  545.     for i := 0 to j-1 do
  546.     begin
  547.       if l <= CollectLen then
  548.       begin
  549.         Inc(l, j + 100);
  550.         SetLength(CollectStr, l);
  551.       end;
  552.       Inc(CollectLen);
  553.       CollectStr[CollectLen] := Buf[i];
  554.       if (CollectLen >= 2) and (CollectStr[CollectLen] = #10) and (CollectStr[CollectLen-1] = #13) then
  555.       begin
  556.         if CollectLen = 2 then
  557.         begin
  558.           CollectEntityBody := True;
  559.           Dec(j, i+1);
  560.           if j > 0 then Move(Buf[i+1], Buf[0], j);
  561.           Break;
  562.         end else
  563.         begin
  564.           Lines.Add(Copy(CollectStr, 1, CollectLen-2));
  565.           CollectLen := 0;
  566.         end;
  567.       end;
  568.     end;
  569.   end;
  570.  
  571.   if CollectEntityBody then
  572.   begin
  573.     if (CollectEntityBody) and (j>0) then
  574.     begin
  575.       i := Length(EntityBody);
  576.       SetLength(EntityBody, i+j);
  577.       Move(Buf, EntityBody[i+1], j);
  578.     end;
  579.     GotEntityBody := ContentLength <= Length(EntityBody);
  580.   end;
  581.   Result := True;
  582. end;
  583.  
  584. constructor TCollector.Create;
  585. begin
  586.   inherited Create;
  587.   Lines := TStringColl.Create;
  588.   Lines.LongString;
  589. end;
  590.  
  591. destructor TCollector.Destroy;
  592. begin
  593.   FreeObject(Lines);
  594.   inherited Destroy;
  595. end;
  596.  
  597.  
  598. procedure TPipeWriteStdThread.Execute;
  599. var
  600.   j: DWORD;
  601.   slen: Integer;
  602. begin
  603.   slen := Length(s);
  604.   if slen > 0 then WriteFile(HPipe, s[1], slen, j, nil);
  605. end;
  606.  
  607. function DoCollect(Collector: TCollector; EntityHeader: TEntityHeader; j: Integer; Buffer: THTTPServerThreadBufer): Boolean;
  608. var
  609.   s,z: string;
  610. begin
  611.   Result := True;
  612.   if not Collector.Collect(Buffer, j) then Result := False else
  613.   if Collector.CollectEntityBody then
  614.   if not Collector.Parsed then
  615.   begin
  616.     Collector.Parsed := True;
  617.     while Collector.LineAvail do
  618.     begin
  619.       s := Collector.GetNextLine;
  620.       if Length(s)<4 then begin Result := False; Break end else
  621.       begin
  622.         GetWrdStrictUC(s, z);
  623.         Delete(z, Length(z), 1);
  624.         if not EntityHeader.Filter(z, s) then
  625.         begin
  626.           // New Feature !!!
  627.         end;
  628.       end;
  629.     end;
  630.     Collector.SetContentLength(StoI(EntityHeader.ContentLength));
  631.   end;
  632. end;
  633.  
  634. procedure TPipeReadErrThread.Execute;
  635. var
  636.   ss: ShortString;
  637.   j: DWORD;
  638. begin
  639.   repeat
  640.     if not ReadFile(HPipe, ss[1], 250, j, nil) then Break;
  641.     ss[0] := Char(j);
  642.     s := s + ss;
  643.   until Terminated;
  644. end;
  645.  
  646.  
  647. procedure TPipeReadStdThread.Execute;
  648. var
  649.   j: DWORD;
  650. begin
  651.   repeat
  652.     if not ReadFile(HPipe, Buffer^, CHTTPServerThreadBufSize, j, nil) then Break;
  653.     Error := not DoCollect(Collector, EntityHeader, j, Buffer^);
  654.     if Error then Break;
  655.     if (Collector.ContentLength > 0) and (Collector.GotEntityBody) then Break;
  656.   until Terminated ;
  657.   j := GetLastError
  658. end;
  659.  
  660. function ExecuteScript(const AExecutable, APath, AScript, AQueryParam, AEnvStr, AStdInStr: string; Buffer: THTTPServerThreadBufer; SelfThr: TThread; var ErrorMsg: string): TEntityHeader;
  661. var
  662.   SI: TStartupInfo;
  663.   PI: TProcessInformation;
  664.   Security: TSecurityAttributes;
  665.   Actually: DWORD;
  666.   si_r, si_w, so_r, so_w, se_r, se_w: THandle;
  667.   b: Boolean;
  668.   Collector: TCollector;
  669.   EntityHeader: TEntityHeader;
  670.   PipeReadStdThread: TPipeReadStdThread;
  671.   PipeWriteStdThread: TPipeWriteStdThread;
  672.   PipeReadErrThread: TPipeReadErrThread;
  673.   s: string;
  674.  
  675. function ReportGUI: string;
  676. var
  677.   d, n, e: string;
  678. begin
  679.   FSPlit(AExecutable, d, n, e);
  680.   Result := n+e+' is a GUI application';
  681. end;
  682.  
  683. begin
  684.   Result := nil;
  685.  
  686.   with Security do
  687.   begin
  688.     nLength := SizeOf(TSecurityAttributes);
  689.     lpSecurityDescriptor := nil;
  690.     bInheritHandle := True;
  691.   end;
  692.  
  693.   CreatePipe(si_r, si_w, @Security, 0);
  694.   CreatePipe(so_r, so_w, @Security, 0);
  695.   CreatePipe(se_r, se_w, @Security, 0);
  696.  
  697.   FillChar(SI, SizeOf(SI), 0);
  698.   SI.CB := SizeOf(SI);
  699.   SI.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  700.   SI.hStdInput := si_r;
  701.   SI.hStdOutput := so_w;
  702.   SI.hStdError := se_w;
  703.   SI.wShowWindow := SW_HIDE;
  704.   if AExecutable = AScript then s := AExecutable else s := AExecutable + ' ' + AScript;
  705.   if AQueryParam <> '' then s := s + ' ' + AQueryParam;
  706.   s := DelSpaces(s);
  707.   b := CreateProcess(
  708.     nil,                     // pointer to name of executable module
  709.     PChar(s),                // pointer to command line string
  710.     @Security,               // pointer to process security attributes
  711.     @Security,               // pointer to thread security attributes
  712.     True,                    // handle inheritance flag
  713.     CREATE_SUSPENDED,        // creation flags
  714.     PChar(AEnvStr),          // pointer to new environment block
  715.     PChar(APath),            // pointer to current directory name
  716.     SI,                      // pointer to STARTUPINFO
  717.     PI                       // pointer to PROCESS_INFORMATION
  718.   );
  719.  
  720.   if b then
  721.   begin
  722.     if WaitForInputIdle(PI.hProcess, 0) = WAIT_TIMEOUT then
  723.     begin
  724.       ErrorMsg := ReportGUI;
  725.       TerminateProcess(PI.hProcess, 0);
  726.       CloseHandle(PI.hThread);
  727.       CloseHandle(PI.hProcess);
  728.       b := False;
  729.     end;
  730.   end else
  731.   begin
  732.     ErrorMsg := SysErrorMsg(GetLastError);
  733.   end;
  734.  
  735.   if not b then
  736.   begin
  737.     CloseHandles([si_r, si_w, so_r, so_w, se_r, se_w]);
  738.     Exit;
  739.   end;
  740.  
  741.   if AStdInStr = '' then
  742.   begin
  743.     PipeWriteStdThread := nil;
  744.   end else
  745.   begin
  746.     PipeWriteStdThread := TPipeWriteStdThread.Create(True);
  747.     PipeWriteStdThread.s := AStdInStr;
  748.     PipeWriteStdThread.HPipe := si_w;
  749.     PipeWriteStdThread.Suspended := False;
  750.   end;
  751.  
  752.   PipeReadErrThread := TPipeReadErrThread.Create(True);
  753.   PipeReadErrThread.HPipe := se_r;
  754.   PipeReadErrThread.Suspended := False;
  755.  
  756.   Collector := TCollector.Create;
  757.   EntityHeader := TEntityHeader.Create;
  758.   PipeReadStdThread := TPipeReadStdThread.Create(True);
  759.   PipeReadStdThread.Priority := tpLower;
  760.   PipeReadStdThread.Collector := Collector;
  761.   PipeReadStdThread.EntityHeader := EntityHeader;
  762.   PipeReadStdThread.Buffer := @Buffer;
  763.   PipeReadStdThread.HPipe := so_r;
  764.   PipeReadStdThread.Suspended := False;
  765.  
  766.   SelfThr.Priority := tpLowest;
  767.  
  768.   ResumeThread(PI.hThread);
  769.   WaitForSingleObject(PI.hProcess, INFINITE);
  770.   CloseHandle(PI.hThread);
  771.   CloseHandle(PI.hProcess);
  772.  
  773. // Close StdIn
  774.   CloseHandle(si_r);
  775.   if PipeWriteStdThread = nil then
  776.   begin
  777.     CloseHandle(si_w);
  778.   end else
  779.   begin
  780.     WaitForSingleObject(PipeWriteStdThread.Handle, INFINITE);
  781.     PipeWriteStdThread.Terminate;
  782.     FreeObject(PipeWriteStdThread);
  783.     CloseHandle(si_w);
  784.   end;
  785.  
  786. // Close StdErr
  787.  
  788.   CloseHandle(se_w);
  789.   PipeReadErrThread.Terminate;
  790.   WaitForSingleObject(PipeReadErrThread.Handle, INFINITE);
  791.   ErrorMsg := PipeReadErrThread.s;
  792.   FreeObject(PipeReadErrThread);
  793.   CloseHandle(se_r);
  794.  
  795. // Close StdOut
  796.   PipeReadStdThread.Terminate;
  797.   CloseHandle(so_w);
  798.   WaitForSingleObject(PipeReadStdThread.Handle, INFINITE);
  799.   SelfThr.Priority := tpNormal;
  800.  
  801.   while not PipeReadStdThread.Error do
  802.   begin
  803.     if not ReadFile(so_r, Buffer, CHTTPServerThreadBufSize, Actually, nil) then Break;
  804.     PipeReadStdThread.Error := not DoCollect(Collector, EntityHeader, Actually, Buffer);
  805.     if (Collector.ContentLength > 0) and (Collector.GotEntityBody) then Break;
  806.   end;
  807.   CloseHandle(so_r);
  808.  
  809.   if PipeReadStdThread.Error or not Collector.GotEntityBody then FreeObject(Collector);
  810.   FreeObject(PipeReadStdThread);
  811.   if Collector = nil then FreeObject(EntityHeader) else
  812.   begin
  813.     if Collector.ContentLength = 0 then
  814.     begin
  815.       Collector.ContentLength := Length(Collector.EntityBody);
  816.       EntityHeader.ContentLength := ItoS(Collector.ContentLength);
  817.     end;
  818.     EntityHeader.CopyEntityBody(Collector);
  819.     FreeObject(Collector);
  820.     Result := EntityHeader;
  821.   end;
  822. end;
  823.  
  824. procedure AddAgentLog(const AAgent: string);
  825. var
  826.   s: string;
  827.   b: DWORD;
  828.   slen: Integer;
  829. begin
  830.   s := AAgent + #13#10;
  831.   EnterCriticalSection(CSAgentLog);
  832.   slen := Length(s);
  833.   WriteFile(HAgentLog, s[1], slen, b, nil);
  834.   LeaveCriticalSection(CSAgentLog);
  835. end;
  836.  
  837.  
  838. procedure AddRefererLog(const ARefererSrc, ARefererDst: string);
  839. var
  840.   s: string;
  841.   b: DWORD;
  842.   slen: Integer;
  843. begin
  844.   if ARefererSrc = '' then Exit;
  845.   s := ARefererSrc + ' -> ' + ARefererDst + #13#10;
  846.   EnterCriticalSection(CSRefererLog);
  847.   slen := Length(s);
  848.   WriteFile(HRefererLog, s[1], slen, b, nil);
  849.   LeaveCriticalSection(CSRefererLog);
  850. end;
  851.  
  852. function CurTime: string;
  853. var
  854.   lt: TSystemTime;
  855.   b: Integer;
  856.   s: string;
  857. begin
  858.   GetLocalTime(lt);
  859.   b := TimeZoneBias;
  860.   if b < 0 then begin b := -b; s := s+'+' end else s := s + '-';
  861.   b := b div 60;
  862.   Result := '['+
  863.         ItoSz(lt.wDay, 2) + '/' +
  864.         MonthE(lt.wMonth) + '/' +
  865.         ItoS(lt.wYear) + ':' +
  866.         ItoSz(lt.wHour,2) + ':' +
  867.         ItoSz(lt.wMinute,2) + ':' +
  868.         ItoSz(lt.wSecond, 2) + ' ' +
  869.         s +
  870.         ItoSz(b div 60, 2) +
  871.         ItoSz(b mod 60, 2) +
  872.         ']';
  873. end;
  874.  
  875. procedure AddAccessLog(const ARemoteHost, ARequestLine, AHTTPVersion, AUserName: string; AStatusCode, ALength: Integer);
  876. var
  877.   authuser,z,k: string;
  878.   b: DWORD;
  879.   slen: Integer;
  880. begin
  881.   if ALength = -1 then z := '-' else z := ItoS(ALength);
  882.   if AHTTPVersion = '' then k := '' else k := ' ' + AHTTPVersion;
  883.   if AUserName = '' then authuser := '-' else authuser := AUserName;
  884.   z := ARemoteHost +  // Remote hostname (or IP number if DNS hostname is not available)
  885.        ' - ' +        // rfc-931
  886.        authuser+' '+  // The username as which the user has authenticated himself
  887.        CurTime+' '+   // Date and time of the request
  888.        '"' + ARequestLine + k + '" ' +  // The request line exactly as it came from the client
  889.        ItoS(AStatusCode) + ' ' + // The HTTP status code returned to the client
  890.        z+             // The content-length of the document transferred
  891.        #13#10;
  892.   EnterCriticalSection(CSAccessLog);
  893.   slen := Length(z);
  894.   WriteFile(HAccessLog, z[1], slen, b, nil);
  895.   LeaveCriticalSection(CSAccessLog);
  896. end;
  897.  
  898. procedure AddErrorLog(const AErr: string);
  899. var
  900.   s: string;
  901.   b: DWORD;
  902.   slen: Integer;
  903. begin
  904.   s := CurTime + ' '+ AErr + #13#10;
  905.   EnterCriticalSection(CSErrorLog);
  906.   slen := Length(s);
  907.   WriteFile(HErrorLog, s[1], slen, b, nil);
  908.   LeaveCriticalSection(CSErrorLog);
  909. end;
  910.  
  911. constructor THttpResponseDataEntity.Create(AEntityHeader : TEntityHeader);
  912. begin
  913.   inherited Create;
  914.   FEntityHeader := AEntityHeader;
  915. end;
  916.  
  917. constructor THttpResponseErrorCode.Create(AErrorCode: Integer);
  918. begin
  919.   inherited Create;
  920.   FErrorCode := AErrorCode;
  921. end;
  922.  
  923. constructor THttpResponseDataFileHandle.Create(AHandle: THandle);
  924. begin
  925.   FHandle := AHandle
  926. end;
  927.  
  928.  
  929. function OpenRequestedFile(const AFName: string; thr: THttpServerThread; d: THttpData): TAbstractHttpResponseData;
  930. var
  931.   I: Integer;
  932.   FHandle: THandle;
  933.   z: string;
  934. begin
  935. // Try to open Requested file
  936.   z := LowerCase(AFName);
  937.   if Copy(z, 1, Length(ParamStr1)) <> LowerCase(ParamStr1) then
  938.   begin
  939.     Result := THttpResponseErrorCode.Create(403);
  940.     Exit;
  941.   end;
  942.   if Copy(z, 1, Length(ParamStr1)+1+Length(ScriptsPath)+1) = ParamStr1+'\'+(ScriptsPath)+'\' then
  943.   begin
  944.     Result := THttpResponseErrorCode.Create(403);
  945.     Exit;
  946.   end;
  947.   FHandle := _CreateFile(AFName, [cRead, cSequentialScan]);
  948.   if FHandle = INVALID_HANDLE_VALUE then
  949.   begin
  950.     AddErrorLog('access to '+AFName+' failed for '+thr.RemoteHost+', reason: '+SysErrorMsg(GetLastError));
  951.     Result := THttpResponseErrorCode.Create(404);
  952.     Exit;
  953.   end;
  954.   if not GetFileNfoByHandle(FHandle, d.FileNfo) then
  955.   begin
  956.     Result := THttpResponseErrorCode.Create(404);
  957.     Exit;
  958.   end;
  959.   z := LowerCase(CopyLeft(ExtractFileExt(AFName),2));
  960.   if z <> '' then
  961.   begin
  962.     if not ContentTypes.Search(@z, I) then z := '' else z := TContentType(ContentTypes.FList^[I]).ContentType;
  963.   end;
  964.   if z = '' then z := 'text/plain';
  965.   d.ResponseEntityHeader := TEntityHeader.Create;
  966.   d.ResponseEntityHeader.ContentType := z;
  967.   d.ResponseEntityHeader.EntityLength := d.FileNfo.Size;
  968.   d.ResponseEntityHeader.LastModified := FileTimeToStr(d.FileNfo.Time);
  969.   d.ResponseGeneralHeader.Date := FileTimeToStr(uGetSystemTime);
  970.   Result := THttpResponseDataFileHandle.Create(FHandle);
  971. end;
  972.  
  973. function GetEnvStr(thr: THttpServerThread; d: THttpData; const PathInfo: string): string;
  974. var
  975.   s: string;
  976.   AuxS: string;
  977.   p: PByteArray;
  978.   j: Integer;
  979.  
  980.   procedure Add(const Name, Value: string); begin s := s + Name+'='+Value+#0 end;
  981.  
  982. begin
  983.   s := '';
  984.   p := Pointer(GetEnvironmentStrings);
  985.   j := 0; while (p^[j]<>0) or (p^[j+1]<>0) do Inc(j);
  986.   Inc(j);
  987.   SetLength(s, j);
  988.   Move(p^, s[1], j);
  989.   FreeEnvironmentStrings(Pointer(p));
  990.   AuxS := PathInfo;
  991.   Replace('\', '/', AuxS);
  992.   if AuxS <> '' then AuxS := '/' + AuxS;
  993.   Add('PATH_INFO', AuxS);
  994.   if AuxS <> '' then AuxS := ParamStr1+'\'+PathInfo;
  995.   Add('PATH_TRANSLATED', AuxS);
  996.   Add('REMOTE_HOST', thr.RemoteHost);
  997.   Add('REMOTE_ADDR', thr.RemoteAddr);
  998.   Add('GATEWAY_INTERFACE', 'CGI/1.1');
  999.   Add('SCRIPT_NAME', d.URIPath);
  1000.   Add('REQUEST_METHOD', d.Method);
  1001.   Add('HTTP_ACCEPT', d.RequestRequestHeader.Accept);                     // Section 14.1
  1002.   Add('HTTP_ACCEPT_CHARSET', d.RequestRequestHeader.AcceptCharset);      // Section 14.2
  1003.   Add('HTTP_ACCEPT_ENCODING', d.RequestRequestHeader.AcceptEncoding);    // Section 14.3
  1004.   Add('HTTP_ACCEPT_LANGUAGE', d.RequestRequestHeader.AcceptLanguage);    // Section 14.4
  1005.   Add('HTTP_FROM', d.RequestRequestHeader.From);                         // Section 14.22
  1006.   Add('HTTP_HOST', d.RequestRequestHeader.Host);                         // Section 14.23
  1007.   Add('HTTP_REFERER', d.RequestRequestHeader.Referer);                   // Section 14.37
  1008.   Add('HTTP_USER_AGENT', d.RequestRequestHeader.UserAgent);              // Section 14.42
  1009.   Add('HTTP_COOKIE', d.RequestRequestHeader.Cookie);
  1010.   Add('QUERY_STRING', d.URIQuery);
  1011.   Add('SERVER_SOFTWARE', CServerName);
  1012.   Add('SERVER_NAME', 'RIT Research Labs');
  1013.   Add('SERVER_PROTOCOL', d.HTTPVersion);
  1014.   Add('SERVER_PORT', ItoS(thr.Socket.FPort));
  1015.   Add('CONTENT_TYPE', d.RequestEntityHeader.ContentType);
  1016.   Add('CONTENT_LENGTH', d.RequestEntityHeader.ContentLength);
  1017.   Add('USER_NAME', d.AuthUser);
  1018.   Add('USER_PASSWORD', d.AuthPassword);
  1019.   Add('AUTH_TYPE', d.AuthType);
  1020.   Result := s + #0;
  1021. end;
  1022.  
  1023. function ReturnNewLocation(const ALocation: string; d: THTTPData): TAbstractHttpResponseData;
  1024. begin
  1025.   d.ResponseResponseHeader.Location := ALocation;
  1026.   Result := THttpResponseErrorCode.Create(302);
  1027. end;
  1028.  
  1029. function IsURL(const s: string): Boolean;
  1030. begin
  1031.   Result := Pos('://', s) > 0;
  1032. end;
  1033.  
  1034. type
  1035.   TExecutableCache = class
  1036.     LocalFName, sResult: string;
  1037.     ReturnValue: HInst;
  1038.   end;
  1039.  
  1040.   TExecutableCacheColl = class(TSortedColl)
  1041.     function Compare(Key1, Key2: Pointer): Integer; override;
  1042.     function KeyOf(Item: Pointer): Pointer; override;
  1043.   end;
  1044.  
  1045. var
  1046.   ExecutableCache: TExecutableCacheColl;
  1047.  
  1048. function TExecutableCacheColl.Compare(Key1, Key2: Pointer): Integer;
  1049. begin
  1050.   Compare := CompareStr(PString(Key1)^, PString(Key2)^);
  1051. end;
  1052.  
  1053. function TExecutableCacheColl.KeyOf(Item: Pointer): Pointer;
  1054. begin
  1055.   Result := @TExecutableCache(Item).LocalFName;
  1056. end;
  1057.  
  1058. function FindExecutableCached(const LocalFName, sPath: string; var s: string): HInst;
  1059. var
  1060.   i: Integer;
  1061.   c: TExecutableCache;
  1062. begin
  1063.   ExecutableCache.Enter;
  1064.   if ExecutableCache.Search(@LocalFName, i) then
  1065.   begin
  1066.     c := ExecutableCache[i];
  1067.     s := StrAsg(c.sResult);
  1068.     Result := c.ReturnValue;
  1069.   end else
  1070.   begin
  1071.     SetLength(s, 1000);
  1072.     Result := FindExecutable(PChar(LocalFName), PChar(sPath), @s[1]);
  1073.     c := TExecutableCache.Create;
  1074.     c.ReturnValue := Result;
  1075.     c.LocalFName := StrAsg(LocalFName);
  1076.     if Result > 32 then
  1077.     begin
  1078.       SetLength(s, NulSearch(s[1]));
  1079.       c.sResult := StrAsg(s);
  1080.     end;
  1081.     ExecutableCache.AtInsert(i, c);
  1082.   end;
  1083.   ExecutableCache.Leave;
  1084. end;
  1085.  
  1086. type
  1087.   TRootCache = class
  1088.     FURI, FResult: string;
  1089.     IsCGI: Boolean;
  1090.   end;
  1091.  
  1092.   TRootCacheColl = class(TSortedColl)
  1093.     function Compare(Key1, Key2: Pointer): Integer; override;
  1094.     function KeyOf(Item: Pointer): Pointer; override;
  1095.   end;
  1096.  
  1097. var
  1098.   RootCacheColl: TRootCacheColl;
  1099.  
  1100.  
  1101. function TRootCacheColl.Compare(Key1, Key2: Pointer): Integer;
  1102. begin
  1103.   Compare := CompareStr(PString(Key1)^, PString(Key2)^);
  1104. end;
  1105.  
  1106. function TRootCacheColl.KeyOf(Item: Pointer): Pointer;
  1107. begin
  1108.   Result := @TRootCache(Item).FURI;
  1109. end;
  1110.  
  1111.  
  1112. function FindRootFileEx(const AURI: string; var IsCGI: Boolean): string;
  1113. var
  1114.   s, z: string;
  1115. begin
  1116.   IsCGI := False;
  1117.   Result := ParamStr1 + AURI + 'index.html';
  1118.   if FileExists(Result) then Exit;
  1119.   Result := ParamStr1 + AURI + 'index.htm';
  1120.   if FileExists(Result) then Exit;
  1121.   Result := ParamStr1 + AURI + 'index.html';
  1122.   s := GetEnvVariable('PATHEXT');
  1123.   while s <> '' do
  1124.   begin
  1125.     GetWrd(s, z, ';');
  1126.     if Length(z) < 2 then Continue;
  1127.     if z[1] <> '.' then Continue;
  1128.     z := ParamStr1+'\'+ScriptsPath+AURI+'index'+z;
  1129.     if FileExists(z) then begin Result := z; IsCGI := True; Exit end;
  1130.   end;
  1131. end;
  1132.  
  1133. function FindRootFile(const AURI: string; var IsCGI: Boolean): string;
  1134. var
  1135.   Found: Boolean;
  1136.   I: Integer;
  1137.   c: TRootCache;
  1138. begin
  1139.   RootCacheColl.Enter;
  1140.   Found := RootCacheColl.Search(@AURI, I);
  1141.   if Found then
  1142.   begin
  1143.     c := RootCacheColl[i];
  1144.     IsCGI := c.IsCGI;
  1145.     Result := StrAsg(c.FResult);
  1146.   end;
  1147.   RootCacheColl.Leave;
  1148.   if Found then Exit;
  1149.   Result := FindRootFileEx(AURI, IsCGI);
  1150.   RootCacheColl.Enter;
  1151.   if not RootCacheColl.Search(@AURI, I) then
  1152.   begin
  1153.     c := TRootCache.Create;
  1154.     c.FURI := StrAsg(AURI);
  1155.     c.FResult := StrAsg(Result);
  1156.     c.IsCGI := IsCGI;
  1157.     RootCacheColl.AtInsert(I, c);
  1158.   end;
  1159.   RootCacheColl.Leave;
  1160.  
  1161. end;
  1162.  
  1163.  
  1164.  
  1165.  
  1166. function WebServerHttpResponse(thr: THttpServerThread; d: THTTPData): TAbstractHttpResponseData;
  1167. var
  1168.   sPath, sName, sExt,
  1169.   s: string;
  1170.   LocalFName: string;
  1171.   ii: HInst;
  1172.   ResponseEntityHeader: TEntityHeader;
  1173.  
  1174.  
  1175. var
  1176.   CgiFile: string;
  1177.   PathInfo: string;
  1178.  
  1179.   // Thanks to Nick McDaniel, Intranaut Inc. (21 January 1999)
  1180.   // We were having problems with files that that had spaces in the name (C:\Program Files\).  The error that was being generated was "Internal Server Error: Can't open
  1181.   // To alievate this problem, we added double quotes to executable and script name
  1182.  
  1183. function QuoteSpaced(const s: string): string;
  1184. begin
  1185.   if Pos(DelSpaces(s), ' ') = 0 then // Does the file name contain space cheracters inside?
  1186.   begin
  1187.     Result := s                 // No, return it as is
  1188.   end else
  1189.   begin
  1190.     Result := '"'+s+'"';        // Yes, add quotes
  1191.   end;
  1192. end;
  1193.  
  1194. procedure Exec;
  1195. begin
  1196.   ResponseEntityHeader := ExecuteScript(QuoteSpaced(s), sPath, QuoteSpaced(CgiFile), d.URIQueryParam, GetEnvStr(thr, d, PathInfo), d.RequestEntityHeader.EntityBody, thr.Buffer, thr, d.ErrorMsg);
  1197. end;
  1198.  
  1199.  
  1200. function CgiFileOK: Boolean;
  1201. var
  1202.   fa: DWord;
  1203.   z: string;
  1204. begin
  1205.   Result := False;
  1206.   fa := GetFileAttributes(PChar(ParamStr1+'\'+ScriptsPath));
  1207.   if fa = INVALID_HANDLE_VALUE then Exit;
  1208.   if (fa and FILE_ATTRIBUTE_DIRECTORY) = 0 then Exit;
  1209.   CgiFile := Copy(LocalFName, 1, Length(ParamStr1)+1+Length(ScriptsPath));
  1210.   PathInfo := CopyLeft(LocalFName, Length(CgiFile)+2);
  1211.   repeat
  1212.     GetWrd(PathInfo, z, '\');
  1213.     CgiFile := CgiFile + '\'+z;
  1214.     fa := GetFileAttributes(PChar(CgiFile));
  1215.     if fa = INVALID_HANDLE_VALUE then Exit;
  1216.     if (fa and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  1217.     begin
  1218.       Result := True;
  1219.       Exit;
  1220.     end;
  1221.   until False;
  1222. end;
  1223.  
  1224. procedure RunCGI;
  1225. begin
  1226.     FSplit(CgiFile, sPath, sName, sExt);
  1227.     if UpperCase(sExt) = '.EXE' then
  1228.     begin
  1229.       s := CgiFile;
  1230.       Exec;
  1231.     end else
  1232.     begin
  1233.       ii := FindExecutableCached(CgiFile, sPath, s);
  1234.       if ii > 32 then
  1235.       begin
  1236.         if not FileExists(s) then
  1237.         begin
  1238.           d.ErrorMsg := SysErrorMsg(GetLastError) + ' ('+s+')';
  1239.         end else
  1240.         begin
  1241.           Exec;
  1242.         end;
  1243.       end else
  1244.       begin
  1245.         if ii = 31 then
  1246.         begin
  1247.           s := CgiFile;
  1248.           Exec;
  1249.         end else
  1250.         begin
  1251.           d.ErrorMsg := SysErrorMsg(ii);
  1252.         end;
  1253.       end;
  1254.     end;
  1255. end;
  1256.  
  1257. procedure MakeHeaders;
  1258. begin
  1259.   if ResponseEntityHeader = nil then
  1260.   begin
  1261.     if d.ErrorMsg = '' then
  1262.     begin
  1263.       d.ErrorMsg := 'CGI script '+d.URIPath+' returned nothing';
  1264.     end else
  1265.     begin
  1266.       d.ErrorMsg := 'Internal Server Error: '+d.ErrorMsg;
  1267.     end;
  1268.     Result := THttpResponseErrorCode.Create(500);
  1269.   end else
  1270.   begin
  1271.     if ResponseEntityHeader.CGILocation <> '' then
  1272.     begin
  1273.       if IsURL(ResponseEntityHeader.CGILocation) then
  1274.       begin
  1275.         Result := ReturnNewLocation(ResponseEntityHeader.CGILocation, d);
  1276.       end else
  1277.       begin
  1278.         Result := OpenRequestedFile(ResponseEntityHeader.CGILocation, thr, d);
  1279.       end;
  1280.     end else
  1281.     begin
  1282.       Result := THttpResponseDataEntity.Create(ResponseEntityHeader);
  1283.     end;
  1284.   end;
  1285. end;
  1286.  
  1287.  
  1288. var
  1289.   IsCGI: Boolean;
  1290.   CheckedURI: string;
  1291. begin
  1292.   ResponseEntityHeader := nil;
  1293.   s := d.URIPath;
  1294.  
  1295.   if Pos('\', s) > 0 then
  1296.   begin
  1297.     Result := THttpResponseErrorCode.Create(403);
  1298.     Exit;
  1299.   end;
  1300.  
  1301.   Replace('/', '\', s);
  1302.   if (s='') or (s[1]<>'\') then
  1303.   begin
  1304.     Result := THttpResponseErrorCode.Create(403);
  1305.     Exit;
  1306.   end;
  1307.   if (Pos('..', s)>0) or
  1308.      (Pos(':',s)>0) or
  1309.      (Pos('\\',s)>0) then
  1310.   begin
  1311.     Result := THttpResponseErrorCode.Create(403);
  1312.     Exit;
  1313.   end;
  1314.  
  1315.   CheckedURI := s;
  1316.   LocalFName := ParamStr1 + CheckedURI;
  1317.  
  1318.  
  1319. // Analyze file extension
  1320.   if LowerCase(Copy(d.URIPath, 2, Length(ScriptsPath)+1)) = (ScriptsPath + '/') then
  1321.   begin
  1322.     if CgiFileOK then RunCGI else d.ErrorMsg := SysErrorMsg(GetLastError);
  1323.     MakeHeaders;
  1324.     Exit;
  1325.   end;
  1326.  
  1327.   if CheckedURI[Length(CheckedURI)]='\' then
  1328.   begin
  1329.     LocalFName := FindRootFile(CheckedURI, IsCGI);
  1330.     if IsCGI then
  1331.     begin
  1332.       CgiFile := LocalFName;
  1333.       RunCGI;
  1334.       MakeHeaders;
  1335.       Exit;
  1336.     end;
  1337.   end else
  1338.   if ExtractFileExt(CheckedURI) = '' then
  1339.   begin
  1340.     Result := ReturnNewLocation(d.URIpath+'/', d);
  1341.     Exit;
  1342.   end;
  1343.  
  1344.  
  1345.   Result := OpenRequestedFile(LocalFName, thr, d);
  1346.  
  1347. end;
  1348.  
  1349. function HttpResponse(thr: THttpServerThread; d: THTTPData): TAbstractHttpResponseData;
  1350. begin
  1351.   Result := WebServerHttpResponse(thr, d);
  1352.   Exit;
  1353. end;
  1354.  
  1355. procedure THTTPServerThread.PrepareResponse(d: THTTPData);
  1356. var
  1357.   r: TAbstractHttpResponseData;
  1358.   rf: THttpResponseDataFileHandle absolute r;
  1359.   re: THttpResponseDataEntity absolute r;
  1360.   rc: THttpResponseErrorCode absolute r;
  1361. begin
  1362.   r := HttpResponse(Self, d);
  1363.   if r = nil then GlobalFail;
  1364.   if r is THttpResponseDataFileHandle then
  1365.   begin
  1366.     d.FHandle := rf.FHandle;
  1367.     d.TransferFile := True;
  1368.     d.ReportError := False;
  1369.     d.StatusCode := 200;
  1370.   end else
  1371.   if r is THttpResponseDataEntity then
  1372.   begin
  1373.     d.ResponseEntityHeader := re.FEntityHeader;
  1374.     d.ReportError := False;
  1375.     d.StatusCode := 200;
  1376.   end else
  1377.   if r is THttpResponseErrorCode then
  1378.   begin
  1379.     d.StatusCode := rc.FErrorCode;
  1380.   end else GlobalFail;
  1381.   FreeObject(r);
  1382. end;
  1383.  
  1384. procedure THTTPServerThread.Execute;
  1385. var
  1386.   FPOS: DWORD;
  1387.   i, j: Integer;
  1388.   s,z,k: string;
  1389.   d: THTTPData;
  1390.   AbortConnection: Boolean;
  1391.   Actually: DWORD;
  1392.  
  1393. begin
  1394.  
  1395.   if not Socket.Handshake then Exit;
  1396.  
  1397.   RemoteAddr := AddrInet(Socket.FAddr);
  1398.   RemoteHost := GetHostNameByAddr(Socket.FAddr);
  1399.  
  1400.   repeat
  1401.     AbortConnection := False;
  1402.     d := THTTPData.Create;
  1403.     d.StatusCode := 400;
  1404.     d.ReportError := True;
  1405.     d.ResponseGeneralHeader := TGeneralHeader.Create;
  1406.     if d.ResponseResponseHeader = nil then d.ResponseResponseHeader := TResponseHeader.Create;
  1407.     s := '';
  1408.     with d do repeat
  1409.  
  1410.       j := Socket.Read(Buffer, CHTTPServerThreadBufSize);
  1411.       if (j <= 0) or (Socket.Status <> 0) then Break;
  1412.  
  1413.       if not RequestCollector.Collect(Buffer, j) then Break;
  1414.       if not RequestCollector.CollectEntityBody then Continue;
  1415.  
  1416.       if not RequestCollector.Parsed then
  1417.       begin
  1418.         if not RequestCollector.LineAvail then Break;
  1419.         RequestCollector.Parsed := True;
  1420.  
  1421.     // Parse the request
  1422.         s := RequestCollector.GetNextLine;
  1423.  
  1424.         if not ProcessQuotes(s) then Break;
  1425.  
  1426.         GetWrdStrictUC(s, Method);    if s = '' then Break;
  1427.         GetWrdStrict(s, RequestURI);  if s = '' then Break;
  1428.         GetWrdStrict(s, HTTPVersion); if s <> '' then Break;
  1429.  
  1430.     // Parse HTTP version
  1431.         s := HTTPVersion;
  1432.         GetWrd(s, z, '/'); if z <> 'HTTP' then Break;
  1433.         GetWrd(s, z, '.');
  1434.         if not DigitsOnly(s) or not DigitsOnly(z) then Break;
  1435.         if not _Val(z, HttpVersionHi) then Break;
  1436.         if not _Val(s, HttpVersionLo) then Break;
  1437.  
  1438.         s := '';
  1439.         z := '';
  1440.  
  1441.         while RequestCollector.LineAvail do
  1442.         begin
  1443.           s := RequestCollector.GetNextLine;
  1444.           if Length(s)<4 then Break;
  1445.           GetWrdStrictUC(s, z);
  1446.           Delete(z, Length(z), 1);
  1447.           if not RequestGeneralHeader.Filter(z, s) and
  1448.              not RequestRequestHeader.Filter(z, s) and
  1449.              not RequestEntityHeader.Filter(z, s) then
  1450.           begin
  1451.             // New Feature !!!
  1452.           end;
  1453.  
  1454.           s := '';
  1455.           z := '';
  1456.         end;
  1457.  
  1458.         if (s <> '') or (z <> '') then Break;
  1459.         RequestCollector.SetContentLength(StoI(RequestEntityHeader.ContentLength));
  1460.       end;
  1461.  
  1462.       if not RequestCollector.GotEntityBody then Continue;
  1463.  
  1464.       // process intity body
  1465.       RequestEntityHeader.CopyEntityBody(RequestCollector);
  1466.  
  1467.       FreeObject(RequestCollector);
  1468.  
  1469.       KeepAlive := UpperCase(RequestGeneralHeader.Connection) = 'KEEP-ALIVE';
  1470.  
  1471.       if (Method <> 'GET') and
  1472.          (Method <> 'POST') and
  1473.          (Method <> 'HEAD') then
  1474.       begin
  1475.         StatusCode := 403;
  1476.         Break;
  1477.       end else
  1478.       begin
  1479.  
  1480.     // Parse URI
  1481.         s := RequestURI;
  1482.         i := Pos('?', s);
  1483.         if i > 0 then
  1484.         begin
  1485.           URIQuery := CopyLeft(s, i+1);
  1486.           DeleteLeft(s, i);
  1487.           if Pos('=', URIQuery) = 0 then
  1488.           begin
  1489.             URIQueryParam := URIQuery;
  1490.             if not UnpackPchars(URIQueryParam) then Break;
  1491.           end;
  1492.         end;
  1493.         i := Pos(';', s);
  1494.         if i > 0 then
  1495.         begin
  1496.           URIParams := CopyLeft(s, i+1);
  1497.           DeleteLeft(s, i);
  1498.         end;
  1499.         if not UnpackPchars(s) then Break;
  1500.         URIPath := s;
  1501.  
  1502.         AddRefererLog(d.RequestRequestHeader.Referer, d.URIPath);
  1503.         AddAgentLog(d.RequestRequestHeader.UserAgent);
  1504.  
  1505.         PrepareResponse(d);
  1506.  
  1507.         Break;
  1508.       end;
  1509.     until False;
  1510.  
  1511.   // Send a response
  1512.     with d do
  1513.     begin
  1514.       if ResponseEntityHeader = nil then ResponseEntityHeader := TEntityHeader.Create;
  1515.  
  1516.       if TransferFile and (RequestRequestHeader.IfModifiedSince <> '') then
  1517.       begin
  1518.         Actually := StrToFileTime(RequestRequestHeader.IfModifiedSince);
  1519.         if (Actually <> INVALID_FILE_TIME) and (StrToFileTime(ResponseEntityHeader.LastModified) = Actually) then
  1520.         begin
  1521.           ZeroHandle(FHandle);
  1522.           TransferFile := False;
  1523.           StatusCode := 304;
  1524.           ReportError := True;
  1525.         end;
  1526.       end;
  1527.  
  1528.       s := ResponseEntityHeader.CGIStatus;
  1529.       if s <> '' then
  1530.       begin
  1531.     k := s;
  1532.     GetWrd(k, z, ' ');
  1533.         Val(z, StatusCode, i);
  1534.     // Status code 200 was treated as error. Thanks to David Gommeren for pointing that out.
  1535.     if StatusCode <> 200 then ReportError := True;
  1536.       end else
  1537.       begin
  1538.  // Get Status Line
  1539.         for i := 0 to MaxStatusCodeIdx do if StatusCode = StatusCodes[i].Code then
  1540.         begin
  1541.           s := StatusCodes[i].Msg;
  1542.           Break;
  1543.         end;
  1544.         if s = '' then GlobalFail;
  1545.         if ErrorMsg = '' then ErrorMsg := s;
  1546.         s := ItoS(StatusCode)+ ' '+ s;
  1547.       end;
  1548.       if ReportError then
  1549.       begin
  1550.         KeepAlive := False;
  1551.         if ResponseEntityHeader.ContentType = '' then ResponseEntityHeader.ContentType := 'text/html';
  1552.         if ResponseEntityHeader.EntityBody = '' then ResponseEntityHeader.EntityBody :=
  1553.           '<HTML>'+
  1554.           '<TITLE>'+s+'</TITLE>'+
  1555.           '<BODY><H1>'+ErrorMsg+'</H1></BODY>'+
  1556.           '</HTML>';
  1557.         ResponseEntityHeader.EntityLength := Length(ResponseEntityHeader.EntityBody);
  1558.       end;
  1559.  
  1560.       ResponseEntityHeader.ContentLength := ItoS(ResponseEntityHeader.EntityLength);
  1561.  
  1562.       if KeepAlive then ResponseGeneralHeader.Connection := 'Keep-Alive';
  1563.  
  1564.       ResponseResponseHeader.Server := CServerName;
  1565.  
  1566.       if ReportError then i := -1 else i := ResponseEntityHeader.EntityLength;
  1567.       AddAccessLog(RemoteHost, Method + ' ' + URIPath, HTTPVersion, d.AuthUser, StatusCode,  i);
  1568.  
  1569.       s := 'HTTP/1.0 '+ s + #13#10+
  1570.         ResponseGeneralHeader.OutString+
  1571.         ResponseResponseHeader.OutString+
  1572.         ResponseEntityHeader.OutString+
  1573.         #13#10;
  1574.  
  1575.       if TransferFile then
  1576.       begin
  1577.         Socket.WriteStr(s);
  1578.         FPOS := 0;
  1579.         repeat
  1580.           ReadFile(FHandle, Buffer, CHTTPServerThreadBufSize, Actually, nil);
  1581.           Inc(FPOS, Actually);
  1582.           if FPOS > FileNfo.Size then Break;
  1583.           if Actually = 0 then Break;
  1584.           Actually := Socket.Write(Buffer, Actually);
  1585.         until (FPOS = FileNfo.Size) or (Actually < CHTTPServerThreadBufSize) or (Socket.Status <> 0);
  1586.         if FPOS <> FileNfo.Size then AbortConnection := True;
  1587.         ZeroHandle(FHandle);
  1588.       end else
  1589.       begin
  1590.         s := s + ResponseEntityHeader.EntityBody;
  1591.         Socket.WriteStr(s);
  1592.       end;
  1593.       AbortConnection := AbortConnection or not KeepAlive;
  1594.     end;
  1595.     FreeObject(d);
  1596.   until AbortConnection
  1597. end;
  1598.  
  1599.  
  1600. function TContentTypeColl.Compare(Key1, Key2: Pointer): Integer;
  1601. begin
  1602.   Compare := CompareStr(PString(Key1)^, PString(Key2)^);
  1603. end;
  1604.  
  1605. function TContentTypeColl.KeyOf(Item: Pointer): Pointer;
  1606. begin
  1607.   Result := @TContentType(Item).Extension;
  1608. end;
  1609.  
  1610. procedure GetContentTypes(const CBase, SubName: string; Swap: Boolean);
  1611. const
  1612.   ClassBufSize = 1000;
  1613. var
  1614.   Buf: array[0..ClassBufSize] of Char;
  1615.   r: TContentType;
  1616.   s, z, t : string;
  1617.   ec,
  1618.   i: Integer;
  1619.   Key,
  1620.   SubKey,
  1621.   BufSize,                       // size of string buffer
  1622.   cSubKeys,                      // number of subkeys
  1623.   cchMaxSubkey,                  // longest subkey name length
  1624.   cchMaxClass,                   // longest class string length
  1625.   cValues,                       // number of value entries
  1626.   cchMaxValueName,               // longest value name length
  1627.   cbMaxValueData,                // longest value data length
  1628.   cbSecurityDescriptor: DWORD;   // security descriptor length
  1629.   ftLastWriteTime: TFileTime;    // last write time
  1630. begin
  1631.   Key := OpenRegKeyEx(CBase, KEY_QUERY_VALUE or KEY_ENUMERATE_SUB_KEYS);
  1632.   BufSize := ClassBufSize;
  1633.   ec := RegQueryInfoKey(
  1634.     Key,                        // handle of key to query
  1635.     @Buf,
  1636.     @BufSize,
  1637.     nil,
  1638.     @cSubKeys,
  1639.     @cchMaxSubkey,
  1640.     @cchMaxClass,
  1641.     @cValues,
  1642.     @cchMaxValueName,
  1643.     @cbMaxValueData,
  1644.     @cbSecurityDescriptor,
  1645.     @ftLastWriteTime);
  1646.   if ec <> ERROR_SUCCESS then
  1647.   begin
  1648.     RegCloseKey(Key);
  1649.     Exit
  1650.   end;
  1651.   for i := 0 to cSubKeys-1 do
  1652.   begin
  1653.     BufSize := ClassBufSize;
  1654.     ec := RegEnumKeyEx(
  1655.       Key,
  1656.       i,
  1657.       Buf,
  1658.       BufSize,
  1659.       nil,
  1660.       nil, // address of buffer for class string
  1661.       nil, // address for size of class buffer
  1662.       @ftLastWriteTime);
  1663.     if ec <> ERROR_SUCCESS then Continue;
  1664.     SetString(s, Buf, BufSize);
  1665.     SubKey := OpenRegKey(CBase+'\'+s);
  1666.     if SubKey = INVALID_REGISTRY_KEY then Continue;
  1667.     z := ReadRegString(SubKey, SubName);
  1668.     RegCloseKey(SubKey);
  1669.     if Swap then
  1670.     begin
  1671.       t := s;
  1672.       s := z;
  1673.       z := t;
  1674.     end;
  1675.     z := LowerCase(CopyLeft(z,2));
  1676.     if (z = '') or (s = '') then Continue;
  1677.     if ContentTypes.Search(@z, ec) then Continue;
  1678.     r := TContentType.Create;
  1679.     r.ContentType := s;
  1680.     r.Extension := z;
  1681.     ContentTypes.AtInsert(ec, r);
  1682.   end;
  1683.   RegCloseKey(Key);
  1684. end;
  1685.  
  1686. type
  1687.   TAdrB = packed record
  1688.     A, B, C, D: Byte;
  1689.   end;
  1690.  
  1691.  
  1692. function _Adr2Int(const s: string): DWORD;
  1693.  
  1694. var
  1695.   CPos: Integer;
  1696.   Error: Boolean;
  1697.  
  1698. function Get: Byte;
  1699. var
  1700.   C: Char;
  1701.   R: Integer;
  1702.   err: Boolean;
  1703. begin
  1704.   Result := 0;
  1705.   if Error then Exit;
  1706.   err := False;
  1707.   R := Ord(S[CPos])-48;
  1708.   Inc(CPos);
  1709.   C := S[CPos];
  1710.   if (C >= '0') and (C <= '9') then
  1711.   begin
  1712.     R := R * 10 + (Ord(C)-48); Inc(CPos);
  1713.     C := S[CPos];
  1714.     if (C >= '0') and (C <= '9') then begin R := R * 10 + (Ord(C)-48); Inc(CPos) end else err := C <> '.';
  1715.   end else err := C <> '.';
  1716.   if (R > 255) or (err) then
  1717.   begin
  1718.     Error := True;
  1719.     Exit;
  1720.   end;
  1721.   Inc(CPos);
  1722.   Result := R;
  1723. end;
  1724.  
  1725. var
  1726.   A: TAdrB;
  1727. begin
  1728.   Error := False;
  1729.   CPos := 1;
  1730.   A.A := Get;
  1731.   A.B := Get;
  1732.   A.C := Get;
  1733.   A.D := Get;
  1734.   if Error then Result := INADDR_NONE else Result := PInteger(@A)^;
  1735. end;
  1736.  
  1737. function Adr2Int(const s: string): Integer;
  1738. begin
  1739.   Result := _Adr2Int(s+'.');
  1740. end;
  1741.  
  1742.  
  1743. var
  1744.   BindPort, BindAddr: DWORD;
  1745.   IsCGI: Boolean;
  1746. function GetHomeDir: Boolean;
  1747. var
  1748.   s: string;
  1749.   i: DWORD;
  1750. begin
  1751.   Result := False;
  1752.   if ParamCount < 1 then
  1753.   begin
  1754.     MessageBox(0, 'Path to home directory is absent!'#13#10+
  1755.                   'See READ.ME for details.'#13#10#13#10+
  1756.                   CServerName+' service failed to start.',
  1757.                   CServerName, CMB_FAILED);
  1758.     Exit;
  1759.   end;
  1760.   ParamStr1 := ParamStr(1);
  1761.   if ParamStr1[Length(ParamStr1)] = '\' then Delete(ParamStr1, Length(ParamStr1), 1);
  1762.   s := FindRootFile('\', IsCGI);
  1763.   if not FileExists(s) then
  1764.   begin
  1765.     s := 'Access to "'+s+'" failed'#13#10'Reason: "'+SysErrorMsg(GetLastError)+'"'#13#10#13#10+
  1766.     CServerName+' service failed to start';
  1767.     MessageBox(0, PChar(s), CServerName, CMB_FAILED);
  1768.     Exit;
  1769.   end;
  1770.   BindPort := 80;
  1771.   BindAddr := _INADDR_ANY;
  1772.   if ParamCount > 1 then
  1773.   begin
  1774.     i := Vl(ParamStr(2));
  1775.     if i <> INVALID_VALUE then BindPort := i;
  1776.   end;
  1777.   if ParamCount > 2 then
  1778.   begin
  1779.     i := Adr2Int(ParamStr(3));
  1780.     if i <> INVALID_VALUE then BindAddr := i;
  1781.   end;
  1782.   Result := True;
  1783. end;
  1784.  
  1785. procedure ReadContentTypes;
  1786. begin
  1787.   ContentTypes := TContentTypeColl.Create;
  1788.   GetContentTypes('SOFTWARE\Classes\MIME\Database\Content Type', 'Extension', False);
  1789.   GetContentTypes('SOFTWARE\Classes', 'Content Type', True);
  1790. end;
  1791.  
  1792. procedure InitLogs;
  1793. begin
  1794.   FAccessLog := 'access_log';
  1795.   FAgentLog := 'agent_log';
  1796.   FErrorLog := 'error_log';
  1797.   FRefererLog := 'referer_log';
  1798.   if not _LogOK(FAccessLog, HAccessLog) or
  1799.      not _LogOK(FAgentLog, HAgentLog) or
  1800.      not _LogOK(FErrorLog, HErrorLog) or
  1801.      not _LogOK(FRefererLog, HRefererLog) then GlobalFail;
  1802.   InitializeCriticalSection(CSAccessLog);
  1803.   InitializeCriticalSection(CSAgentLog);
  1804.   InitializeCriticalSection(CSErrorLog);
  1805.   InitializeCriticalSection(CSRefererLog);
  1806. end;
  1807.  
  1808. procedure InitReseterThread;
  1809. begin
  1810.   SocketsColl := TColl.Create;
  1811.   ResetterThread := TResetterThread.Create;
  1812. end;
  1813.  
  1814. procedure FreeDummyLibraries;
  1815. var
  1816.   I: Integer;
  1817. begin
  1818.   I := GetModuleHandle('OleAut32'); if I <> 0 then FreeLibrary(I); 
  1819.   I := GetModuleHandle('Ole32'); if I <> 0 then FreeLibrary(I);
  1820.   I := GetModuleHandle('RPCRT4'); if I <> 0 then FreeLibrary(I);
  1821.   I := GetModuleHandle('AdvAPI32'); if I <> 0 then FreeLibrary(I);
  1822.   I := GetModuleHandle('GDI32'); if I <> 0 then FreeLibrary(I);
  1823.   I := GetModuleHandle('COMCTL32'); if I <> 0 then FreeLibrary(I);
  1824.   I := GetModuleHandle('USER32'); if I <> 0 then FreeLibrary(I);
  1825. end;
  1826.  
  1827. procedure MainLoop;
  1828. var
  1829.   J, err: Integer;
  1830.   NewSocketHandle,
  1831.   ServerSocketHandle: WinSock.TSocket;
  1832.   NewSocket: TSocket;
  1833.   NewThread: THTTPServerThread;
  1834.   WData: TWSAData;
  1835.   Addr: TSockAddr;
  1836.   s: string;
  1837. begin
  1838.   err := WSAStartup(MakeWord(1,1), WData);
  1839.   if err <> 0 then
  1840.   begin
  1841.     s := 'Failed to initialize WinSocket,error #'+ItoS(err);
  1842.     MessageBox(0, PChar(s), CServerName, CMB_FAILED);
  1843.     Halt;
  1844.   end;
  1845.   ServerSocketHandle := socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  1846.   if ServerSocketHandle = INVALID_SOCKET then
  1847.   begin
  1848.     s := 'Failed to create a socket, Error #'+ItoS(WSAGetLastError);
  1849.     MessageBox(0, PChar(s), CServerName, CMB_FAILED);
  1850.     Halt;
  1851.   end;
  1852.  
  1853.   Addr.sin_family := AF_INET;
  1854.   Addr.sin_port := htons(BindPort);
  1855.   Addr.sin_addr.s_addr := BindAddr;
  1856.   if bind(ServerSocketHandle, Addr, SizeOf(Addr)) = SOCKET_ERROR then
  1857.   begin
  1858.     S := 'Failed to bind the socket, error #'+ItoS(WSAGetLastError)+'.'#13#10#13#10+
  1859.          'Probable reason is that another daemon is already running on the same port ('+ItoS(BindPort)+').';
  1860.     MessageBox(0, PChar(S), CServerName, CMB_FAILED);
  1861.     Halt;
  1862.   end;
  1863.  
  1864.  
  1865.   InitReseterThread;
  1866.  
  1867.   listen(ServerSocketHandle, 5);
  1868.  
  1869.   FreeDummyLibraries;
  1870.  
  1871.   repeat
  1872.     J := SizeOf(Addr);
  1873.     {$IFDEF VER90}
  1874.     NewSocketHandle := accept(ServerSocketHandle, Addr, J);
  1875.     {$ELSE}
  1876.     NewSocketHandle := accept(ServerSocketHandle, @Addr, @J);
  1877.     {$ENDIF}
  1878.     if NewSocketHandle = INVALID_SOCKET then Exit;
  1879.     NewSocket := TSocket.Create;
  1880.     NewSocket.Handle := NewSocketHandle;
  1881.     NewSocket.FAddr := Addr.sin_addr.s_addr;
  1882.     NewSocket.FPort := Addr.sin_port;
  1883.     if not NewSocket.Startup then FreeObject(NewSocket) else
  1884.     begin
  1885.       SocketsColl.Enter;
  1886.       if SocksCount = 0 then
  1887.       begin
  1888.         ResetterThread.TimeToSleep := SleepQuant;
  1889.         SetEvent(ResetterThread.oSleep);
  1890.       end;
  1891.       Inc(SocksCount);
  1892.       SocketsColl.Leave;
  1893.       NewThread := THTTPServerThread.Create;
  1894.       NewThread.FreeOnTerminate := True;
  1895.       NewThread.Socket := NewSocket;
  1896.       NewSocket.RegisterSelf;
  1897.       NewThread.Resume;
  1898.     end;
  1899.   until False;
  1900.   CloseSocket(ServerSocketHandle);
  1901. end;
  1902.  
  1903.  
  1904.  
  1905. procedure ComeOn;
  1906. var
  1907.   i: Integer;
  1908. begin
  1909.  
  1910. //--- Set Hight priority class
  1911. //  SetPriorityClass(GetCurrentProcess, HIGH_PRIORITY_CLASS);
  1912.  
  1913. //--- Initialize xBase Module
  1914.   xBaseInit;
  1915.  
  1916.   ExecutableCache := TExecutableCacheColl.Create;
  1917.   ExecutableCache.Enter;
  1918.   ExecutableCache.Leave;
  1919.  
  1920.   RootCacheColl := TRootCacheColl.Create;
  1921.   RootCacheColl.Enter;
  1922.   RootCacheColl.Leave;
  1923.  
  1924. //--- Get and validate a home directory
  1925.   if not GetHomeDir then Exit;
  1926.  
  1927.  
  1928.  
  1929. //--- Read content types from registry and associate with file extensions
  1930.   ReadContentTypes;
  1931.  
  1932. // --- Open log files and initialize semaphores
  1933.   InitLogs;
  1934.  
  1935. // --- Perform main loop
  1936.   MainLoop;
  1937.  
  1938. // Non-debug version never exits :-)
  1939.  
  1940.   ResetterThread.Terminate;
  1941.   SetEvent(ResetterThread.oSleep);
  1942.   SocketsColl.Enter;
  1943.   for i := 0 to SocketsColl.Count-1 do shutdown(TSocket(SocketsColl[i]).Handle, 2);
  1944.   SocketsColl.Leave;
  1945.   while SocketsColl.Count > 0 do Sleep(1000);
  1946.   ResetterThread.TimeToSleep := SleepQuant;
  1947.   SetEvent(ResetterThread.oSleep);
  1948.   WaitForSingleObject(ResetterThread.Handle, INFINITE);
  1949.   FreeObject(ResetterThread);
  1950.   FreeObject(SocketsColl);
  1951.   FreeObject(ContentTypes);
  1952.   xBaseDone;
  1953.   CloseHandle(HAccessLog);
  1954.   CloseHandle(HAgentLog);
  1955.   CloseHandle(HErrorLog);
  1956.   CloseHandle(HRefererLog);
  1957.   DeleteCriticalSection(CSAccessLog);
  1958.   DeleteCriticalSection(CSAgentLog);
  1959.   DeleteCriticalSection(CSErrorLog);
  1960.   DeleteCriticalSection(CSRefererLog);
  1961. end;
  1962.  
  1963. end.
  1964.  
  1965.  
  1966.